package reflect;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * @author wangyingjie
 * @date
 * @remark 自定义类加载器(父类都是系统类加载器SystemClassLoader)
 */
public class MyClassLoader extends ClassLoader {
    public static void main(String[] args) {
        System.out.println("12313123");
    }

    /**
     * 在类加载器加载之前就编译源文件
     * @param name 文件路径
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class clazz = null;
        String replaceName = name.replace(".", "/");
        String javaFileName = replaceName + ".java";
        String classFileName = replaceName + ".class";
        File javaFile = new File(javaFileName);
        File classFile = new File(classFileName);
        //如果java源文件存在，且class文件不存在，或者java源文件的修改时间比class文件修改时间晚，重新编译
        if (javaFile.exists() && (!classFile.exists()|| javaFile.lastModified()>classFile.lastModified())){
            try {
                //如果编译失败或者classFile不存在
                if (!compile(javaFileName) || !classFile.exists()){
                    throw new ClassNotFoundException("ClassNotFound:"+javaFileName);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //如果class文件存在，则读入二进制数据到内存
        if (classFile.exists()){
            try {
                byte[] bytes = getBytes(classFileName);
                clazz = defineClass(name,bytes,0,bytes.length);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //clazz是否成功读取
        if (clazz == null) {
            throw new ClassNotFoundException(name);
        }

        return clazz;
    }

    /**
     * 编译
     * @param fileName
     * @return
     * @throws IOException
     */
    private boolean compile(String fileName) throws IOException {
        System.out.println("MyClassLoader正在编译："+fileName+"...");
        //调用javac命令编译
        Process p = Runtime.getRuntime().exec("javac" + fileName);
        try {
            //其他线程等待
            p.waitFor();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int ret = p.exitValue();//获取线程退出值
        return ret==0;
    }

    /**
     * 把文件读到二进制数组里去
     * @param fileName
     * @return
     * @throws IOException
     */
    private byte[] getBytes(String fileName) throws IOException {
        File file = new File(fileName);
        long len = file.length();
        byte[] raw = new byte[(int) len];
        FileInputStream fis = new FileInputStream(file);
        //一次性都读到raw的byte数组里去
        int i = fis.read(raw);
        if (i != len){
            throw new IOException("无法一次性读取:"+fileName);
        }
        fis.close();
        return raw;
    }

}
